home *** CD-ROM | disk | FTP | other *** search
/ Programming Sound Cards / Programming Sound Cards.iso / sound_56 / dmastp9b.asm < prev    next >
Assembly Source File  |  1995-01-01  |  17KB  |  435 lines

  1. ;══════════════════════════════════════════════════════════════════════════════
  2. ; Play Stereo On SoundBlaster PRO
  3. ;   André Baresel (with some help from Craig Jackson)
  4. ;──────────────────────────────────────────────────────────────────────────────
  5. ; STATUS: DOES WORK ON SBPRO - Cool Mono Sound (CMS) (tm) on SB16
  6. ;──────────────────────────────────────────────────────────────────────────────
  7. ; Requirements: 80286, SoundBlaster Pro (see BASEADDR,DMA channel,IRQ number)
  8. ; Resolutions : 8-bit / stereo / 4..22khz
  9. ; Parameters  : none
  10. ; Note        : Before compiling this call first "VOC2RAW TEST1.VOC /I /R"
  11. ;         to creat "TEST1.INC" in this case a file with 8bit mono signed data
  12. ;
  13. ; ■ DSP command 40h  ... set sample rate
  14. ; ■ DSP command 48h  ... set DMA block size
  15. ; ■ DSP command 90h  ... play highspeed DMA 8bit mono autoinit
  16. ; ■ DSP command D1h  ... Enable Speaker
  17. ; ■ DSP command D3h  ... Disable Speaker
  18. ;
  19.  
  20. .MODEL small
  21. .286
  22. ; CONSTANTS ───────────────────────────────────────────────────────────────────
  23.  
  24. ; SoundBlaster SETUP
  25. BASEADDR           EQU 0220h       ;SoundBlaster base address
  26. IRQ7               EQU 15          ;SoundBlaster IRQ
  27. DMAchannel         EQU 1           ;SoundBlaster DMA channel
  28.  
  29. ; PIC MASKS FOR MASK/DEMASK IRQ
  30. PICANDMASK         EQU 01111111b   ;'AND' PIC mask for clear IRQ7
  31. PICORMASK          EQU 10000000b   ;'OR' PIC mask for set IRQ7
  32.  
  33. ; DMA CONTROLLER REGISTERS :
  34. WRITEMASK          EQU 00ah         ;WRITE MASK REGISTER
  35. WRITEMODE          EQU 00bh         ;WRITE MODE REGISTER
  36. CLEARFLIPFLOP      EQU 00ch
  37. PAGE_CHN           EQU 083h         ;PAGE REGISTER FOR DMAchannel 1
  38. BASE_CHN           EQU 002h         ;BASEADDRESS REGISTER DMAchannel 1
  39. COUNT_CHN          EQU 003h         ;COUNT REGISTER DMAchannel 1
  40.  
  41. ; SAMPLERATE : (if you change it pay attention to maximum samplerate)
  42. TIMECONST          EQU 211          ; = 22222 Hz (256-1000000/22000)
  43.  
  44. ; DMA WRITE MODE
  45. WANTEDMODE         EQU 01011000b    ; singlemode, autoinit, readmode
  46.  
  47. ;──────────────────────────────────────────────────────────────────────────────
  48. ; MACRO DEFINITIONs
  49. ;──────────────────────────────────────────────────────────────────────────────
  50. STARTUP                 MACRO
  51. ; MASM 5.x COMPATIBILITY
  52. __start:                mov     ax,DGROUP
  53.                         mov     ds,ax
  54.                         mov     bx,ss
  55.                         sub     bx,ax
  56.                         shl     bx,004h
  57.                         mov     ss,ax
  58.                         add     sp,bx
  59. ENDM
  60.  
  61. WAITWRITE               MACRO
  62. LOCAL                   loopWait,endloop
  63. ;          Arguments : DX = Status port (BASEADDR+0Ch)
  64. ;          Returns   : n/a
  65. ;          Destroys  : AL
  66.  
  67.                         push    cx
  68.                         xor     cx,cx           ; need that for slow SBs !
  69. loopWait:               dec     cx
  70.                         jz      endloop
  71.                         in      al,dx           ; AL = WRITE COMMAND STATUS
  72.                         or      al,al
  73.                         js      loopWait        ; Jump if bit7=1 - writing not allowed
  74. endloop:                pop     cx
  75. ENDM
  76.  
  77. WAITREAD                MACRO
  78. LOCAL                   loopWait,endloop
  79. ;          Arguments : DX = Status port   (normaly BASEADDR+0Eh)
  80. ;          Returns   : n/a
  81. ;          Destroys  : AL
  82.  
  83.                         push    cx
  84.                         xor     cx,cx           ; need that for slow SBs !
  85. loopWait:               dec     cx
  86.                         jz      endloop
  87.                         in      al,dx           ; AL = DATA AVAILABLE STATUS
  88.                         or      al,al
  89.                         jns     loopWait        ; Jump if bit7=0 - no data available
  90. endloop:                pop     cx
  91. ENDM
  92.  
  93. RESET_DSP               MACRO
  94. local                   SBthere
  95. ;          Arguments : n/a
  96. ;          Returns   : n/a
  97. ;          Destroys  : DX,AL
  98.  
  99.                         mov      dx,BASEADDR+06h
  100.                         mov      al,1
  101.                         out      dx,al          ; start DSP reset
  102.  
  103.                         in       al,dx
  104.                         in       al,dx
  105.                         in       al,dx
  106.                         in       al,dx          ; wait 3 µsec
  107.  
  108.                         xor      al,al
  109.                         out      dx,al          ; end DSP Reset
  110.  
  111.                         add      dx,08h         ; dx = DSP DATA AVAILABLE
  112.                         WAITREAD
  113.                         sub      dx,4           ; dx = DSP Read Data
  114.                         in       al,dx
  115.                         cmp      al,0aah        ; if there is a SB then it returns 0AAh
  116.                         je       SBthere
  117.                         jmp      RESET_ERROR    ; No SB - exit program
  118. SBthere:
  119. ENDM
  120. ;─── End of Macrodefinitions ──────────────────────────────────────────────────
  121.  
  122. .STACK 100h
  123.  
  124. .DATA
  125. ;──────────────────────────────────────────────────────────────────────────────
  126. ; Creat TEST1.INC with calling "VOC2RAW TEST1.VOC /I /R" or creat your own
  127. ; textfile with sampledata
  128.  
  129. SAMPLEBUFFER LABEL BYTE        
  130.     INCLUDE TEST1.INC
  131. SAMPLEBUFFEREND LABEL BYTE
  132.  
  133.     PART                db 1
  134.  
  135.     information         db 13,10,'DMASTP9B.EXE - play 8bit unsign stereo data (only on SB PRO)'
  136.                         db 13,10,'On a SB16 you',39,'ll here cool mono sound :)'
  137.                         db 13,10,'Stop playing with <ESC>.','$'
  138.     memerror            db 13,10,'Not enough memory to creat the DMA buffer','$'
  139.     txtpart0            db 13,10,'playing part 0','$'
  140.     txtpart1            db 13,10,'playing part 1','$'
  141.     sberror             db 13,10,'No SoundBlaster at this BASEADDR ! PROGRAM HALTED.','$'
  142.  
  143.     OLDInterruptSEG     dw ?
  144.     OLDInterruptOFS     dw ?
  145.  
  146.     DMAbufferOFS        dw ?
  147.     DMAbufferPage       db ?
  148.  
  149.     SAMPLEBUFFERLENGTH = offset SAMPLEBUFFEREND - offset SAMPLEBUFFER
  150. ;──────────────────────────────────────────────────────────────────────────────
  151. .CODE
  152.  STARTUP
  153.  
  154.            ; FIRST FREE NOT USED MEMORY :
  155.            mov     bx,ss
  156.            mov     ax,es
  157.            sub     bx,ax
  158.  
  159.            mov     ax,sp
  160.            add     ax,15
  161.  
  162.            shr     ax,4
  163.  
  164.            add     bx,ax
  165.            mov     ah,04ah
  166.            int     21h
  167.  
  168.            ; NOW ALLOCATE DMABUFFER
  169.            mov     bx,SAMPLEBufferlength+15
  170.            shr     bx,3            ; cx = samplebufferlength*2/16
  171.                                    ; (count of 16byte blocks)
  172.            mov     ah,48h
  173.            int     21h
  174.            jnc     enoughmem       ; ok got the memory
  175.            mov     dx,offset memerror
  176.            mov     ah,9
  177.            int     21h             ; WRITE MSG 2 SCRN THAT THERE'S NOT ENOUGH MEM
  178.            jmp     return2dos
  179. enoughmem: ; AX = segment of DMA buffer / offset = 0
  180.  
  181. ;──────────────────────────────────────────────────────────────────────────────
  182. ; calculate page and offset for DMAcontroller :
  183. ;
  184. ; segment*16+offset = 20bit memory location -> upper 4 bits  = page
  185. ;                                              lower 16 bits = offset
  186. ;──────────────────────────────────────────────────────────────────────────────
  187.            rol     ax,4
  188.            mov     bl,al
  189.            and     bl,00fh
  190.            and     al,0f0h
  191.            mov     [DMABufferOFS],ax
  192.            mov     [DMAbufferPage],bl
  193. ;──────────────────────────────────────────────────────────────────────────────
  194. ; check for DMApage override :
  195. ; ... problem: DMA controller separates memory into 64KB pages, you can only
  196. ; transfer data is placed in one page - no page overrides are allowed
  197. ;──────────────────────────────────────────────────────────────────────────────
  198. ; To solve that :
  199. ; creat a DMA buffer with double size you want - if the first part is placed
  200. ; on a page border the second part is for sure not
  201. ;──────────────────────────────────────────────────────────────────────────────
  202.            mov     cx,SAMPLEBUFFERLENGTH
  203.            neg     ax          ; ax = 65536 - ax   (bytes left to DMA page border)
  204.            cmp     ax,cx
  205.            ja      nooverride
  206.  
  207.            ; USE SECOND PART :
  208.            neg     ax               ; ax = offset first data
  209.            add     ax,cx            ; use second part
  210.            inc     ax             ; cx+1 bytes to next part ;)
  211.            mov     [DMABufferOFS],ax
  212.            inc     [DMABufferPage]  ; 2nd part is on next page !
  213. nooverride:
  214.  
  215. ;──────────────────────────────────────────────────────────────────────────────
  216. ; Now move the sampledata into the DMAbuffer
  217. ; in a MODplayer you'll do that in the IRQ,
  218. ; but in our case we only repeat the same sound in the DMAbuffer
  219. ;──────────────────────────────────────────────────────────────────────────────
  220.            ; FIRST CALCULATE THE DOS SEG/OFS FROM DMAPage/OFS
  221.            mov     al,byte ptr [DMABufferOFS]
  222.            and     al,0fh
  223.            xor     ah,ah
  224.            mov     di,ax       ; di = offset of DMAbuffer
  225.            mov     ax,[DMABufferOFS]
  226.            and     al,0f0h
  227.            or      al,[DMABufferPage]
  228.            ror     ax,4
  229.            mov     es,ax       ; es = segment of DMABuffer
  230.            mov     si,offset samplebuffer
  231.  
  232.            ; DS:SI - samples in dataseg
  233.            ; ES:DI - DMABuffer
  234.            ; CX - count of samples
  235.            ;
  236.            rep movsb
  237.  
  238.            RESET_DSP
  239.  
  240.            ; WRITE INFOMRATION TO SCREEN :
  241.            mov     dx,offset information
  242.            mov     ah,9
  243.            int     21h                  ; write program information to screen
  244.  
  245.            ; ENABLE SB SPEAKERS (for all SBs <SB16)
  246.            mov     dx,BASEADDR+00Ch            ;DX = DSP Write Data or Command
  247.            WAITWRITE
  248.            mov     al,0D1h                     ; AL = Enable speaker
  249.            out     dx,al                       ; Output: DSP Write Data or Command
  250.  
  251.            ; SETUP IRQ :
  252.            xor     ax,ax
  253.            mov     es,ax                       ; es to page 0 (Interrupt table)
  254.            mov     si,IRQ7*4                   ; si = position in interrupt table
  255.  
  256.            ; DISABLE IRQ (if it was enabled somehow)
  257.            in      al,021h
  258.            and     al,PICANDMASK               ; SET MASK REGISTER BIT TO DISABLE INTERRUPT
  259.            out     021h,al
  260.  
  261.            ; CHANGE POINTER IN INTERRUPT TABLE
  262.            mov     ax,es:[si]
  263.            mov     [OLDInterruptOFS],ax        ; save offset of old interupt vector for restoring
  264.            mov     ax,OFFSET OWN_IRQ
  265.            mov     es:[si],ax                  ; set offset of new interrupt routine
  266.            mov     ax,es:[si+2]
  267.            mov     [OLDInterruptSEG],ax        ; save segment of old interupt vector for restoring
  268.            mov     ax,cs
  269.            mov     es:[si+2],ax                ; set segment of new interrupt routine
  270.  
  271.            ; CHANGE PIC MASK :
  272.            in      al,021h
  273.            and     al,PICANDMASK   ; CLEAR MASK REGISTER BIT TO ENABLE INTERRUPT
  274.            out     021h,al
  275.  
  276.  
  277.            mov     cx,SAMPLEBUFFERLENGTH-1
  278.  
  279. ;──────────────────────────────────────────────────────────────────────────────
  280. ; Setup DMA-controller :
  281. ;
  282. ; 1st  MASK DMA CHANNEL
  283. ;
  284.            mov     al,DMAchannel
  285.            add     al,4
  286.            out     WRITEMASK,al
  287. ;──────────────────────────────────────────────────────────────────────────────
  288. ; 2nd  CLEAR FLIPFLOP
  289. ;
  290.            out     CLEARFLIPFLOP,al
  291. ;──────────────────────────────────────────────────────────────────────────────
  292. ; 3rd  WRITE TRANSFER MODE
  293. ;
  294.            mov     al,WANTEDMODE
  295.            add     al,DMAchannel
  296.            out     WRITEMODE,al
  297. ;──────────────────────────────────────────────────────────────────────────────
  298. ; 4th  WRITE PAGE NUMBER
  299. ;
  300.            mov     al,[DMAbufferPage]
  301.            out     PAGE_CHN,al
  302. ;──────────────────────────────────────────────────────────────────────────────
  303. ; 5th  WRITE DMA BASEADDRESS
  304. ;
  305.            mov     ax,[DMABufferOFS]
  306.            out     BASE_CHN,al
  307.            mov     al,ah
  308.            out     BASE_CHN,al
  309. ;──────────────────────────────────────────────────────────────────────────────
  310. ; 6th  WRITE BASECOUNTER = SAMPLELENGTH-1
  311. ;
  312.            mov     al,cl
  313.            out     COUNT_CHN,al
  314.            mov     al,ch
  315.            out     COUNT_CHN,al
  316. ;──────────────────────────────────────────────────────────────────────────────
  317. ; 7th  DEMASK CHANNEL
  318. ;
  319.            mov     al,DMAchannel
  320.            out     WRITEMASK,al
  321.  
  322. ;──────────────────────────────────────────────────────────────────────────────
  323. ; Setup SoundBlaster :
  324. ;
  325. ; 1st  SET TIMECONSTANT
  326. ;
  327.            mov     dx,BASEADDR+00Ch            ;DX = DSP Write Data or Command
  328.            WAITWRITE
  329.            mov     al,040h                     ;AL = Set timeconstant
  330.            out     dx,al
  331.            WAITWRITE
  332.            mov     al,TIMECONST
  333.            out     dx,al
  334.  
  335. ;──────────────────────────────────────────────────────────────────────────────
  336. ; 2nd  SETUP STEREO MODE
  337. ;
  338.            mov     dx,BASEADDR+004h            ; DX = DSP Mixerchip Select Register
  339.            mov     al,0eh                      ; output/stereo select register
  340.            out     dx,al
  341.            inc     dx                          ; DX = DSP Mixerchip write data
  342.            mov     al,13h                      ; stereo / no filter
  343.            out     dx,al
  344. ;──────────────────────────────────────────────────────────────────────────────
  345. ; 3rd  SETUP DMABUFFER SIZE
  346. ;
  347.            mov     dx,BASEADDR+00Ch            ;DX = DSP Write Data or Command
  348.            WAITWRITE
  349.            mov     al,048h                     ;AL = DMA DAC 8bit autoinit
  350.            out     dx,al
  351.            mov     cx,SAMPLEBUFFERLENGTH
  352.            shr     cx,1                        ; after every half buffer report
  353.            dec     cx
  354.            WAITWRITE
  355.            mov     al,cl                       ;AL = LOWER PART SAMPLELENGTH
  356.            out     dx,al
  357.            WAITWRITE
  358.            mov     al,ch                       ;AL = HIGHER PART SAMPLELENGTH
  359.            out     dx,al
  360. ;──────────────────────────────────────────────────────────────────────────────
  361. ; 4th  USE HIGHSPEED AUTOINIT
  362. ;
  363.            WAITWRITE
  364.            mov     al,01Ch
  365.            out     dx,al
  366.  
  367. ; TRANSFER STARTs.....NOW...... :)
  368.  
  369. waitloop:  mov     ah,01                       ;AH = Check for character function
  370.            int     016h                        ;   Interrupt: Keyboard
  371.            jz      waitloop                    ; wait for a key (sound in background)
  372.            xor     ah,ah                       ;Read character, flush keypress
  373.            int     016h                        ;   Interrupt: Keyboard
  374.            cmp     al,27
  375.            jne     waitloop                    ; if <ESC> do exit
  376.  
  377. exit:      RESET_DSP
  378.  
  379.            ; RESTORE PIC MASK
  380.            in      al,021h
  381.            or      al,PICORMASK                ;<-- SET REGISTER MASK BITS TO DISABLE
  382.            out     021h,al
  383.  
  384.            ; RESTORE IRQ :
  385.            xor     ax,ax
  386.            mov     es,ax                       ; es to page 0 (Interrupt table)
  387.            mov     si,IRQ7*4
  388.            mov     ax,[OLDInterruptOFS]
  389.            mov     es:[si],ax                  ; set old interrupt routine
  390.            mov     ax,[OLDInterruptSEG]
  391.            mov     es:[si+2],ax
  392.  
  393.            ; CLEAR KEYBUFFER
  394.            mov     ah,01
  395.            int     16h
  396.            jz      return2dos
  397.            xor     ah,ah                       ;Read character, flush keypress
  398.            int     016h                        ;   Interrupt: Keyboard
  399.  
  400.            ; TERMINATE EXE:
  401. return2dos:
  402.            mov     ax,04c00h
  403.            int     21h
  404.  
  405. ; display information if Soundblaster is not on this baseaddress
  406. RESET_ERROR:
  407.            mov     dx,offset sberror
  408.            mov     ah,9
  409.            int     21h                         ; text output
  410.            jmp     return2dos
  411.  
  412. ;──────────────────────────────────────────────────────────────────────────────
  413. ; Our own IRQ for detecting buffer half SB currently plays
  414. ; It's generated by the SoundBlaster hardware
  415. ;──────────────────────────────────────────────────────────────────────────────
  416. OWN_IRQ:   pusha
  417.            mov     dx,BASEADDR+00Eh            ;DX = DSP DATA AVAILABLE (IRQ ACKNOWLEDGE)
  418.            in      al,dx
  419.            mov     ax,@data
  420.            mov     ds,ax
  421.            mov     dx,offset txtpart0
  422.            cmp     [part],0
  423.            je      notpart1
  424.            mov     dx,offset txtpart1
  425. notpart1:  mov     ah,9
  426.            int     21h             ; text output
  427.            neg     [part]
  428.            inc     [part]          ; part = 1-part  result : 0,1,0,1,0,....
  429.            mov     al,020h
  430.            out     020h,al                     ;ACKNOWLEDGE HARDWARE INTERRUPT
  431.            popa
  432.            IRET
  433.  
  434. END     __start
  435.